package snapex.core.security;

import java.io.IOException;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import lombok.extern.slf4j.Slf4j;
import snapex.core.security.JwtTokenStoreConfiguration.JwtTokenStoreCondition;

@Slf4j
@Configuration
@Conditional(JwtTokenStoreCondition.class)
public class JwtTokenStoreConfiguration {

	@Bean
	@ConditionalOnMissingBean(ResourceServerTokenServices.class)
	public DefaultTokenServices jwtTokenServices(TokenStore jwtTokenStore) {
		log.info("**********jwtTokenServices**********");
		DefaultTokenServices services = new DefaultTokenServices();
		services.setTokenStore(jwtTokenStore);
		return services;
	}

	@Bean
	@ConditionalOnMissingBean(TokenStore.class)
	public TokenStore jwtTokenStore() {
		log.info("**********jwtTokenStore**********");
		return new JwtTokenStore(overrideDefaultJwtTokenEnhancer());
	}

	@Value("${snapex.security.jwtVerifierKeyPath}")
	String jwtVerifierKeyPath;
	
	@Bean
	@Primary
	public JwtAccessTokenConverter overrideDefaultJwtTokenEnhancer() {
		
		log.info("*********@Primary JwtAccessTokenConverter, jwtVerifierKeyPath:{}",jwtVerifierKeyPath);
		
		Resource resource = null;
		if(jwtVerifierKeyPath.startsWith("classpath:")) {
			resource = new ClassPathResource(jwtVerifierKeyPath.replaceFirst("classpath:",""));
		}else {
			resource = new FileSystemResource(jwtVerifierKeyPath);
		}
		
		JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
				
		try {			
			String publicKey = new String(IOUtils.toByteArray(resource.getInputStream()));
			converter.setVerifierKey(publicKey);
		}
		catch(IOException e) {
			throw new RuntimeException("Failed to read public key cert", e);
		}
		
		return converter;
	}

	static class JwtTokenStoreCondition extends SpringBootCondition {

		@Override
		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {

			ConditionMessage.Builder message = ConditionMessage.forCondition("tokenStore Condition");
			Environment environment = context.getEnvironment();
//			if("true".equalsIgnoreCase(environment.getProperty("snapex.security.enabled"))) {
				String tokenStore = environment.getProperty("snapex.security.tokenStore");
				if ("JWT".equals(tokenStore)) {
					return ConditionOutcome.match(message.foundExactly("JWT tokenStore"));
				}
//			}

			return ConditionOutcome.noMatch(message.didNotFind("demo.security.tokenStore").atAll());
		}
	}
}
